package jerry;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStreamWriter;
import java.net.ServerSocket;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.StringTokenizer;

/**
 * This class is used to protect with passwords the statistics: every couple of username and
 * password will generate a *SERVER* ID, that will be used in logs changing the name of a player
 * adding (ID), starting from 1.
 * 
 * @author Alby87
 */
public class UsersManager {
	private Integer Number = 0;

	private Hashtable<String, Hashtable<String, Integer>> Users;

	private final Object lock1 = new Object();

	private final Object lock2 = new Object();

	private ServerSocket ss = null;

	private String userDBDir = null;
	@SuppressWarnings("unchecked")
	public UsersManager(ServerSocket ss, String userDBDir) throws IOException,
			ClassNotFoundException {
		this.ss = ss;
		this.userDBDir = userDBDir;
		Number = 0;
		File file = new File(userDBDir + File.separatorChar + "ut_passwords.db");
		if (file.createNewFile() == false) {
			FileInputStream fis = new FileInputStream(file);
			ObjectInputStream ois = new ObjectInputStream(fis);
			Users = (Hashtable<String, Hashtable<String, Integer>>) ois
					.readObject();
			ois.close();
		} else {
			Users = new Hashtable<String, Hashtable<String, Integer>>();
		}
	}

	public void addThread() {
		synchronized (lock1) {
			Number++;
		}
	}

	public void deleteThread() {
		synchronized (lock1) {
			Number--;
		}
	}

	public Integer lockupPlayer(String name, String password) {
		if (!(Users.containsKey(name))) {
			Hashtable<String, Integer> newPlayerPassword = new Hashtable<String, Integer>();
			synchronized (lock2) {
				newPlayerPassword.put(password, 1);
				Users.put(name, newPlayerPassword);
			}
		}
		Hashtable<String, Integer> Part1 = Users.get(name);
		Integer ID;
		if (!(Part1.containsKey(password))) {
			ID = Part1.size() + 1;
			synchronized (lock2) {
				Part1.put(password, ID);
				Users.put(name, Part1);
			}
		}
		ID = Part1.get(password);
		return ID;
	}

	public void reWriteFile(String name) throws IOException {
		System.out.println("Parsing Player's password");
		LinkedList<String> elements = new LinkedList<String>();
		String readed;
		String work;
		String work2;
		String[] newLine = new String[9];
		Integer ID = 0;
		int quantity = 0;
		StringTokenizer st;
		Boolean saveOnList = false;
		Boolean unloadStack = false;
		File f = new File(name);
		FileInputStream stream = new FileInputStream(f);
		BufferedReader br = new BufferedReader(new InputStreamReader(stream));
		String newName = name.substring(0, (name.length() - 5));
		File fOut = new File(newName);
		FileOutputStream outStream = new FileOutputStream(fOut);
		BufferedWriter bw = new BufferedWriter(
				new OutputStreamWriter(outStream));
		while (true) {
			readed = br.readLine();
			if (readed == null)
				break;
			st = new StringTokenizer(readed, "\u0009");
			work = st.nextToken(); // time
			if (!(work.equals("\u0000")))
				work = st.nextToken(); // general category
			if (work.equals("\u0000p\u0000l\u0000a\u0000y\u0000e\u0000r\u0000")) {
				work = st.nextToken(); // minor category
				if (work
						.equals("\u0000R\u0000e\u0000n\u0000a\u0000m\u0000e\u0000")) {
					saveOnList = true;
				}
				if (work
						.equals("\u0000C\u0000o\u0000n\u0000n\u0000e\u0000c\u0000t\u0000")) {
					work = st.nextToken();// player name
					st.nextToken();// player code for the game
					st.nextToken();// booleadn I don't know
					work2 = st.nextToken();// player password
					if (work2
							.equals("\u0000I\u0000s\u0000A\u0000B\u0000o\u0000t\u0000"))
						ID = 0;
					else if (work2
							.equals("\u0000N\u0000o\u0000C\u0000h\u0000e\u0000c\u0000k\u0000s\u0000u\u0000m\u0000"))
						ID = -1;
					else
						ID = this.lockupPlayer(work, work2);
					unloadStack = true;
				}
			}
			if (saveOnList) {
				elements.addLast(readed);
			} else {
				bw.write(readed);
				bw.newLine();
			}
			while (unloadStack) {
				newLine = new String[9];
				readed = elements.removeLast();
				st = new StringTokenizer(readed, "\u0009");
				newLine[0] = st.nextToken(); // time
				if (!(newLine[0].equals("\u0000")))
					newLine[1] = st.nextToken(); // general category
				else {
					newLine[1] = "nothing";
				}
				if (newLine[1]
						.equals("\u0000p\u0000l\u0000a\u0000y\u0000e\u0000r\u0000")) {
					newLine[2] = st.nextToken(); // minor category
					if (newLine[2]
							.equals("\u0000R\u0000e\u0000n\u0000a\u0000m\u0000e\u0000")) {
						newLine[3] = st.nextToken();// player name
						newLine[4] = st.nextToken();// player ID in the match
						if (ID == 0) {
							bw.write(readed);
							bw.newLine();
						} else {
							quantity = readed.indexOf(newLine[3])
									+ newLine[3].length();
							work2 = readed.substring(0, quantity);
							work2 = work2.concat("-\u0000" + encode(ID)
									+ readed.substring(quantity));
							bw.write(work2);
							bw.newLine();
						}
					} else {
						if (newLine[2]
								.equals("\u0000C\u0000o\u0000n\u0000n\u0000e\u0000c\u0000t\u0000")) {
							newLine[3] = st.nextToken();// player name
							newLine[4] = st.nextToken();// player code for the game
							newLine[5] = st.nextToken();// boolean I don't know
							newLine[6] = st.nextToken();// player password
							if (ID == 0) {
								bw.write(readed);
								bw.newLine();
							} else {
								quantity = readed.indexOf(newLine[3])
										+ newLine[3].length();
								work2 = readed.substring(0, quantity);
								work2 = work2.concat("-\u0000" + encode(ID)
										+ readed.substring(quantity));
								bw.write(work2);
								bw.newLine();
							}
							unloadStack = false;
							saveOnList = false;
						} else {
							bw.write(readed);
							bw.newLine();
						}
					}
				} else {
					bw.write(readed);
					bw.newLine();
				}
			}
			if (work
					.equals("\u0000g\u0000a\u0000m\u0000e\u0000_\u0000e\u0000n\u0000d\u0000"))
				break;
		}
		bw.flush();
		bw.close();
		br.close();
		f.delete();
	}

	public void save() throws IOException, InterruptedException {
		int NumberReaded = 0;
		ss.close();
		for (int Counter = 0; Counter < 7; Counter++) {
			synchronized (lock1) {
				NumberReaded = Number;
			}
			if (NumberReaded != 0) {
				if (Counter < 6) {
					System.out.println("Error: there are " + NumberReaded
							+ " thread still running: forcing close in "
							+ (6 - Counter) * 10 + " seconds.");
					Thread.sleep(10000);
				}
				if (Counter == 6)
					System.out.println("Forcing close");
			}
		}
		FileOutputStream fos = new FileOutputStream(userDBDir + File.separatorChar
				+ "ut_passwords.db");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(Users);
		oos.close();
	}

	public String encode(Integer toEncode) {
		String toEncodeString = "(\u0000E\u0000R\u0000R\u0000)\u0000";
		char[] characters = new char[3];
		if (toEncode == -1) {
			toEncodeString = new String("(\u0000N\u0000P\u0000W\u0000)\u0000");
		}
		if ((toEncode != -1) && (toEncode < 10)) {
			toEncodeString = new String("(\u00000\u00000\u0000"
					+ toEncode.toString() + "\u0000)\u0000");
		}
		if ((toEncode > 9) && (toEncode < 100)) {
			characters = toEncode.toString().toCharArray();
			toEncodeString = new String("(\u00000\u0000" + characters[0]
					+ "\u0000" + characters[1] + "\u0000)\u0000");
		}
		if ((toEncode > 99) && (toEncode < 1000)) {
			characters = toEncode.toString().toCharArray();
			toEncodeString = new String("(\u0000" + characters[0] + "\u0000"
					+ characters[1] + "\u0000" + characters[2]
					+ "\u0000)\u0000");
		}
		if (toEncode > 999) {
			toEncodeString = new String("(\u0000O\u0000F\u0000W\u0000)\u0000");
		}
		return toEncodeString;
	}
	
	public String getUserDBDir() {
		return userDBDir;
	}
}